package com.yanqu.road.server.connect;

import com.yanqu.road.NettyMgr;
import com.yanqu.road.connect.Connection;
import com.yanqu.road.connect.YanQuConnection;
import com.yanqu.road.pb.server.LoadProto;
import com.yanqu.road.connect.ServerConnectionGroup;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.server.protocol.Cross3Protocol;
import com.yanqu.road.utils.ConfigHelper;
import io.netty.channel.ChannelHandlerContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ChannelCross3LinkedControl {
    private static Logger logger = LogManager.getLogger(ChannelCross3LinkedControl.class.getName());

    private static Map<Long, Integer> crossCountMap = new ConcurrentHashMap<>();
    private static Map<Long, Map<Integer, ServerConnectionGroup>> crossServerLinkedSet = new ConcurrentHashMap<>();
    private static Object lockObj = new Object();
    private static int checkIndex = 0;

    private ChannelCross3LinkedControl() {

    }

    public static void init(long serverId, int count) {
        crossCountMap.put(serverId, count);
    }

    public static void removeLinkedClient(ChannelHandlerContext ctx) {
        ServerConnectionGroup client = getLinkedClient(ctx);
        if (client == null) {
            return;
        }

        if (!crossServerLinkedSet.containsKey(client.getServerId())) {
            return;
        }
        int id = Long.valueOf(client.getConnectionId()).intValue();
        logger.error("removeLinkedClient --- removeId : {}", id);
        if (crossServerLinkedSet.get(client.getServerId()).containsKey(id)) {
            crossServerLinkedSet.get(client.getServerId()).remove(id);
        }

    }

    public static ServerConnectionGroup getLinkedClient(ChannelHandlerContext ctx) {
        ServerConnectionGroup client = ctx.channel().attr(NettyMgr.LINKED_CLIENT).get();
        return client;
    }

    public static void send(long serverId, long userId, YanQuMessage packet) {
        if (!crossServerLinkedSet.containsKey(serverId) || !crossCountMap.containsKey(serverId)) {
            logger.info("Can not found cross connection , serverId = " + serverId);
            return;
        }

//        if (userId <= 0) {
//            sendSingle(serverId, packet);
//        } else {

        if (userId < 0) {
            userId = 0;
        }

            int idx = (int) (userId % crossCountMap.get(serverId));
            ServerConnectionGroup group = crossServerLinkedSet.get(serverId).get(idx);
            if (group != null) {
                group.sendPacket(userId, packet);
            } else {
                logger.error("Can not found cross connection , serverId = " + serverId + " , userId = " + userId
                        + " code : " + packet.getCode() + " . packet forward failed." + " .idx : " + idx
                        + "  .crossLinkedSet size:" + crossServerLinkedSet.get(serverId).size());
            }
//        }
    }

    public static void sendToAll(YanQuMessage packet) {
        if (crossCountMap != null) {
            for (long serverId : crossCountMap.keySet()) {
                send(serverId, 0, packet);
            }
        }
    }

    private static void sendSingle(long serverId, YanQuMessage packet) {
        for (ServerConnectionGroup group : crossServerLinkedSet.get(serverId).values()) {
            group.write(packet.getPlayerId(), packet);
            break;
        }
    }

    public static void send(long serverId, YanQuMessage packet) {
        send(serverId, packet.getPlayerId(), packet);
    }

    public static void sendAll(YanQuMessage packet) {
        for (Map<Integer, ServerConnectionGroup> agentLinkedSet : crossServerLinkedSet.values()) {
            for (ServerConnectionGroup group : agentLinkedSet.values()) {
                group.write(packet.getPlayerId(), packet);
            }
        }
    }

    public static ServerConnectionGroup addCrossLinkedGroup(long serverId, int serverIndex, YanQuConnection connection, int count, int linkIndex) {
        synchronized (lockObj) {
            if (!crossServerLinkedSet.containsKey(serverId)) {
                crossServerLinkedSet.put(serverId, new HashMap<>());
            }

            if (crossServerLinkedSet.get(serverId).containsKey(serverIndex)) {
                crossServerLinkedSet.get(serverId).get(serverIndex).addConnection(connection, linkIndex);
            } else {
                ServerConnectionGroup group = new ServerConnectionGroup(count);
                group.setServerId(serverId);
                group.addConnection(connection, linkIndex);
                crossServerLinkedSet.get(serverId).put(serverIndex, group);
            }
            return crossServerLinkedSet.get(serverId).get(serverIndex);
        }
    }

    public static void syncCheckMsgToGate(int serverType) {
        LoadProto.CheckCrossConnectMsg.Builder builder = LoadProto.CheckCrossConnectMsg.newBuilder();
        builder.setServerType(serverType);
        builder.setId(ConfigHelper.getInt("serverId"));
        for (Map<Integer, ServerConnectionGroup> map : crossServerLinkedSet.values()) {
            if (map.containsKey(0)) {
                YanQuMessage message = YanQuMessageUtils.buildMessage(Cross3Protocol.C3_CROSS_HEART_CHECK, builder);
                map.get(0).connectionSendMsg(message);
            }
        }
        checkIndex++;
    }

    public static void closeConnection(ChannelHandlerContext ctx) {
        ServerConnectionGroup client = getLinkedClient(ctx);
        if (client == null) {
            return;
        }

        if (!crossServerLinkedSet.containsKey(client.getServerId())) {
            return;
        }
        int id = Long.valueOf(client.getConnectionId()).intValue();
        logger.error("removeLinkedClient --- removeId : {}", id);
        if (crossServerLinkedSet.get(client.getServerId()).containsKey(id)) {
            Connection[] connections = crossServerLinkedSet.get(client.getServerId()).get(id).getAllConnections();
            for (Connection connection : connections) {
                logger.error("s remoteAddress: {}, d remoteAddress: {}", ctx.channel().remoteAddress(), ((YanQuConnection) connection).getChannel().remoteAddress());
                if (ctx.channel().remoteAddress().equals(((YanQuConnection) connection).getChannel().remoteAddress())) {
                    connection.close();
                    logger.error("remove connection Address : {}", ctx.channel().remoteAddress());
                    break;
                }
            }
            boolean isAllDown = true;
            for (Connection connection : connections) {
                if (connection.isAlive()) {
                    isAllDown = false;
                    break;
                }
            }
            if (isAllDown) {
                synchronized (lockObj) {
                    crossServerLinkedSet.get(client.getServerId()).remove(id);
                    logger.error("remove client serverId : {}", client.getServerId());
                }
            }
        }
    }
}
